Contents

1 Dimensionality reduction and clustering on scRNA-seq data

We will work on scRNA-seq data of mouse gastrulation and early organogenesis from Pijuan-Sala et al., 2019. This Shiny application provides an interactive interface that allows users to validate their own analysis on this data. You can reach the original data and related scripts from the Github page.

Gastrulation is a phase early in the embryonic development of most animals, during which the single-layered blastula is reorganized into a multilayered structure known as the gastrula (Wikipedia, 2020-06-02).

1.1 Getting familiar with the pre-processed data

Let’s start with including required libraries.

  suppressPackageStartupMessages({
  library(Seurat)
  library(ggplot2)
  library(dplyr)
  library(data.table)
  library(cowplot)
  set.seed(1)
})

In the previous practical, we have learned the essential preprocessing steps for working with scRNA-seq. Here we will start working with the preprocessed version of the mouse gastrulation scRNA-seq data. We will load the pre-saved Seurat object of this data.

Warning: The pre-processed mouse gastrulation scRNA-seq Seurat object is large (2GBs). In today’s practical, we will subset the data for fast computation. You can, however, work on the original data if you want to practise what we learned today. The meta-data is provided seperately.

    mgsc <- readRDS('data/gastrulation/mgsc.rds')
    mgsc
## An object of class Seurat 
## 29452 features across 116312 samples within 1 assay 
## Active assay: RNA (29452 features, 0 variable features)

Reminder: number of rows = genes = features, and number of columns = cells = samples

    # let's see the available slots
    slotNames(mgsc)
##  [1] "assays"       "meta.data"    "active.assay" "active.ident" "graphs"      
##  [6] "neighbors"    "reductions"   "project.name" "misc"         "version"     
## [11] "commands"     "tools"

Now let’s look at to the metadata table of this dataset that contains an overview of the samples.

    # see it is empty for now
    head(mgsc@meta.data, 5)
## data frame with 0 columns and 5 rows
    # now let's load the metadata 
    metadata <- fread('data/gastrulation/sample_metadata.txt.gz') %>% .[stripped==FALSE & doublet==FALSE]
    
    # and add  the metadata  to our seurat object
    mgsc <- AddMetaData(mgsc, metadata = data.frame(metadata, row.names = metadata$cell))
    
    # now let's see once more
    head(mgsc@meta.data, 5)
##          cell        barcode sample stage sequencing.batch doublet stripped
## cell_1 cell_1 AAAGGCCTCCACAA      1  E6.5                1   FALSE    FALSE
## cell_2 cell_2 AACAAACTCGCCTT      1  E6.5                1   FALSE    FALSE
## cell_5 cell_5 AACAGAGAATCAGC      1  E6.5                1   FALSE    FALSE
## cell_6 cell_6 AACATATGAATCGC      1  E6.5                1   FALSE    FALSE
## cell_8 cell_8 AACCGATGGCTTCC      1  E6.5                1   FALSE    FALSE
##                celltype      umapX      umapY        celltype2    celltype3
## cell_1         Epiblast -10.227546 -2.8816875         Epiblast  Epiblast-PS
## cell_2 Primitive_Streak  -6.625458  0.1089605 Primitive_Streak  Epiblast-PS
## cell_5     ExE_ectoderm  10.061009 -0.0293132     ExE_ectoderm ExE_ectoderm
## cell_6         Epiblast -10.454418 -0.2694517         Epiblast  Epiblast-PS
## cell_8         Epiblast -11.047206 -2.2052687         Epiblast  Epiblast-PS

Now we are able to see the annotations (e.g. cell type) for each cell. There is also a column named stage which shows the embryonic day the cells were sequenced. To speed up our experiments, we will work on a subset of cells that belong to stage E6.75.

    mgsc_subset <- mgsc[ , mgsc@meta.data$stage=='E6.75']
    mgsc_subset
## An object of class Seurat 
## 29452 features across 2075 samples within 1 assay 
## Active assay: RNA (29452 features, 0 variable features)
    # now lets save this subset
    saveRDS(mgsc_subset, file = "data/gastrulation/mgsc_e675.rds")
    
    mgsc <- mgsc_subset

This is where you are starting from! :) Now you can load mgsc_e675.rds object to start working! Make sure that the object you loaded has same numbers of columns and rows with E6.75 Seurat object.

Q1: Let’s warm up! Can you try to subset cells in locations \(1, 23, 515\) and genes in locations \(21, 44, 116\), respectively?

Answer

    s1 <- mgsc[, c(1,23,515)] # Subset to cells 1, 23, 515
    s2 <- mgsc[c(21,44,116), ] # Subset to genes 21, 44, 116 
    
    s1
## An object of class Seurat 
## 29452 features across 3 samples within 1 assay 
## Active assay: RNA (29452 features, 0 variable features)
    s2
## An object of class Seurat 
## 3 features across 2075 samples within 1 assay 
## Active assay: RNA (3 features, 0 variable features)

We can use rownames and colnames to see the names of genes and cells.

# rownames = gene names
head(rownames(mgsc))
## [1] "ENSMUSG00000051951" "ENSMUSG00000089699" "ENSMUSG00000102343"
## [4] "ENSMUSG00000025900" "ENSMUSG00000025902" "ENSMUSG00000104328"
# colnames = sample/cell names
head(colnames(mgsc))
## [1] "cell_5456" "cell_5457" "cell_5458" "cell_5459" "cell_5460" "cell_5461"

Q2: Observe the count data of the first 15 cells for genes “ENSMUSG00000051951” and “ENSMUSG00000033845”. (Hint: use GetAssayData function. )

Answer

GetAssayData(object = mgsc)[c("ENSMUSG00000051951", "ENSMUSG00000033845"), 1:15]
## 2 x 15 sparse Matrix of class "dgCMatrix"
##    [[ suppressing 15 column names 'cell_5456', 'cell_5457', 'cell_5458' ... ]]
##                                                                        
## ENSMUSG00000051951 .        .        .        .       .        .       
## ENSMUSG00000033845 1.590889 3.265748 3.693841 2.28663 3.645026 2.568868
##                                                                         
## ENSMUSG00000051951 .        .        .        .        .        .       
## ENSMUSG00000033845 2.251156 2.997522 3.052133 2.881758 2.380428 3.334173
##                                             
## ENSMUSG00000051951 .       .        .       
## ENSMUSG00000033845 1.34505 2.986037 2.199593
# mgsc[c("ENSMUSG00000051951", "ENSMUSG00000033845"), 1:15]@assays$RNA@counts

The . values in the matrix represent \(0\)s. Since most values in an scRNA-seq matrix are \(0\), Seurat uses a sparse-matrix representation to speed up calculations and reduce memory usage.

2 Dimensionality reduction

2.1 Feature selection: identification of highly variable features

Q3:Our feature set (i.e. number of genes) are quite large. What kind of features do you think should be in the dataset?

Answer We will choose a subset of features that have high cell-to-cell variation in the dataset (i.e, genes that are highly expressed in some cells, and lowly expressed in others) which can help highlighting biological signal.

Seurat implements FindVariableFeatures to model the mean-variance relationship in single-cell data which reduces to dimensions to 2000 by default. We will update nfeatures argument to reduce dimensionality to 1000.

mgsc <- FindVariableFeatures(mgsc, selection.method = "vst", nfeatures = 1000)
options(repr.plot.width=12, repr.plot.height=6)

# Identify the 10 most highly variable genes
top10 <- head(VariableFeatures(mgsc), 10)

# plot variable features 
plot1 <- VariableFeaturePlot(mgsc)
LabelPoints(plot = plot1, points = top10, repel = TRUE,  xnudge = 0, ynudge = 0)
## Warning: Using `as.character()` on a quosure is deprecated as of rlang 0.3.0.
## Please use `as_label()` or `as_name()` instead.
## This warning is displayed once per session.
## Warning: Transformation introduced infinite values in continuous x-axis

head(HVFInfo(mgsc)[VariableFeatures(mgsc), ], 5)
##                         mean variance variance.standardized
## ENSMUSG00000032083 10.008193 971.0756              24.49617
## ENSMUSG00000024990  3.765301 210.4092              22.38176
## ENSMUSG00000061808  7.784578 546.4217              21.09222
## ENSMUSG00000024503  6.853976 419.2819              19.63029
## ENSMUSG00000024391  3.319036 112.9888              15.76248

2.1.1 Scaling the data

Q4: Seurat implements the ScaleData function to scale the data. Do you remember why we need this step?

mgsc <- ScaleData(mgsc) #Vector of features names to scale/center. Default is variable features.
## Centering and scaling data matrix

The results are stored inmgsc[["RNA"]]@scale.data.

mgsc[["RNA"]]@scale.data[1:5,1:5]
##                     cell_5456  cell_5457  cell_5458  cell_5459  cell_5460
## ENSMUSG00000025902 -0.3250561 -0.3250561 -0.3250561 -0.3250561 -0.3250561
## ENSMUSG00000042182 -0.0428776 -0.0428776 -0.0428776 -0.0428776 -0.0428776
## ENSMUSG00000026124 -0.3533363 -0.3533363 -0.3533363 -0.3533363 -0.3533363
## ENSMUSG00000008136 -0.3580923 -0.3580923 -0.3580923 -0.3580923 -0.3580923
## ENSMUSG00000025993 -0.3715563 -0.3715563 -0.3715563 -0.3715563 -0.3715563
# OR
# mgsc@assays$RNA@scale.data[1:5,1:5]

2.2 Principal Component Analysis

Let’s use RunPCA function of the Seurat. Pay attention to the features argument. How many features will PCA work on?

mgsc <- RunPCA(mgsc, npcs = 100, features = VariableFeatures(object = mgsc)) 

We can explore the constructed embeddings via mgsc@reductions.

    slotNames(mgsc@reductions[["pca"]])
## [1] "cell.embeddings"            "feature.loadings"          
## [3] "feature.loadings.projected" "assay.used"                
## [5] "global"                     "stdev"                     
## [7] "key"                        "jackstraw"                 
## [9] "misc"
    dim(mgsc@reductions[["pca"]]@cell.embeddings)
## [1] 2075  100
    mgsc@reductions[["pca"]]@cell.embeddings[1:5,1:5]
##                PC_1      PC_2       PC_3      PC_4        PC_5
## cell_5456 -7.525323 -2.044025 -1.7048600 -3.297759 -0.00652879
## cell_5457 -8.266647 -3.636625  0.6928462 -3.041104 -0.18143907
## cell_5458 -7.687170 -3.591576 -2.1696897 -3.157680 -0.03483741
## cell_5459 -7.256699 -2.657866 -1.7883639 -3.313753 -1.10164595
## cell_5460 -7.410198 -2.796104 -1.9718807 -4.048726 -0.79348906
    # OR
    # Embeddings(mgsc, reduction = "pca")[1:5, 1:5]

Q5: Do you remember what kind of information feature.loadings store?

Answer feature.loadings: stores the weight for each feature along each dimension of the embedding.
  # (1) we can visualize top genes associated with pca embeddings
  VizDimLoadings(mgsc, dims = 1:2, reduction = "pca")

  VizDimLoadings(mgsc, dims = 3:4, reduction = "pca")

2.2.1 Determine the ‘dimensionality’ of the dataset

Q6: Name that one method that we used to determine the dimensionality. Let’s try to find out how much of the variance is explained by the PCs. (Hint: check slotNames(mgsc@reductions[["pca"]]) which feature might help you with this. )

Answer

percent <- 100*mgsc@reductions[["pca"]]@stdev^2/sum(mgsc@reductions[["pca"]]@stdev^2)

perc_data <- data.frame(percent=percent, PC=1:length(percent))
ggplot(perc_data, aes(x=PC, y=percent)) + 
  geom_line(aes(y= percent), linetype=2) + 
  scale_x_continuous(breaks=seq(from=0, to=80, by= 10)) +
  geom_text(aes(label=round(percent, 2)), size=2, vjust=-.5) + 
  ylim(0, 80)

# OR you can directly use Seurat function 
ElbowPlot(mgsc,  ndims = 80)

Q7: Now it’s time to visualize our data! Generate the following output: three 2D plots with the first six PCs and print them side by side. i.e. PC1-PC2, PC3-PC4, PC5-PC6. (Hint: Utilize DimPlot and plot_grid functions.)

Answer

  # (2) 
  plot_grid(ncol = 3,
    DimPlot(mgsc, reduction = "pca", dims = 1:2) + theme(legend.position="none"),
    DimPlot(mgsc, reduction = "pca", dims = 3:4) + theme(legend.position="none"),
    DimPlot(mgsc, reduction = "pca", dims = 5:6) + theme(legend.position="none") )

2.3 Non-linear dimensional reduction (t-SNE/UMAP)

2.3.1 t-SNE

Seurat comprises RunTSNE which uses Rtsne library (which we previously worked with in the lecture).

# we are using the PCA embeddings as our input
mgsc <- RunTSNE(mgsc, reduction="pca", dims = 1:30,  nthreads = 4, max_iter = 2000)
mgsc@reductions
## $pca
## A dimensional reduction object with key PC_ 
##  Number of dimensions: 100 
##  Projected dimensional reduction calculated:  FALSE 
##  Jackstraw run: TRUE 
##  Computed using assay: RNA 
## 
## $tsne
## A dimensional reduction object with key tSNE_ 
##  Number of dimensions: 2 
##  Projected dimensional reduction calculated:  FALSE 
##  Jackstraw run: FALSE 
##  Computed using assay: RNA
DimPlot(mgsc, reduction = "tsne")

Q8:Do you remember one of the important hyper-parameters of t-SNE? Print t-SNE plots using DimPlot and explore how structure of the printed data changes with this parameter. Keep dimension as \(30\). (Hint: reduction.name will help you save your embeddings that are produced by different configurations. )

2.3.2 UMAP

mgsc <- RunUMAP(mgsc, dims = 1:30)
DimPlot(mgsc, reduction = "umap")

Homework: How about the important parameters for UMAP? Print following UMAP plots using DimPlot and explore how structure of the visualized data changes with these two parameters. Choose dimensions between \(1:30\).

Homework: Plot the projections of the dataset with three of the dimensionality reduction techniques printed side by side and explore AugmentPlot.

3 Cluster the cells

For our subset of time point E6.5, we have the cell type annotation information. Although this is not usually the case in many computational problems, i.e. we start without knowing (or having a very vague idea of) how many clusters we will end up. For now let’s take advantage of the known annotations.

Q9: How many \(real\) classes we have? Reproduce the following plots colored by cell type. ( Hint: You can make use group.by argument in DimPlot to extract stored cluster IDs.)

##  [1] "Epiblast"                       "Primitive_Streak"              
##  [3] "ExE_endoderm"                   "Visceral_endoderm"             
##  [5] "ExE_ectoderm"                   "Nascent_mesoderm"              
##  [7] "Anterior_Primitive_Streak"      "PGC"                           
##  [9] "Mixed_mesoderm"                 "Rostral_neurectoderm"          
## [11] "Surface_ectoderm"               "Def._endoderm"                 
## [13] "Haematoendothelial_progenitors"

3.1 K-means

Seurat does not support K-means? What are we going to do now!? :)

Q10: What do we need to run \(K-means\)? Try to reproduce the following plot where true classes are compared to k-means clusters (\(k=13\)). ( Hint: Make use of Embeddings function or utilize reductions we previously used in the previous examples. You can save cluster IDs to mgsc@meta.data field. )

## [1] 2075   30

Homework: Explore how clusters would have changed if you used t-SNE embeddings instead of PCA as an input to k-means algorithm.

## [1] 2075    2

3.1.1 How many clusters should we choose?

We already know the number of cell-types in our data. But this is not always the case. How would we decide the number of clusters if we didn’t know the number of classes? Which \(k\)-means feature did we look at the to decide number of clusters?

Homework: Print the following plot using the same embeddings from the previous example. (Hint: Lecture notes:))

Can we decide the number of clusters looking at this plot?

3.2 Hierarchical clustering

Seurat does not support hierarchical clustering too? But we can do it!!

Q11:Which elements do we need to perform hierarchical clustering? Try to reproduce the following dendogram. Use Euclidian as distance metric and ward.D2 as linkage method.

Looking at the dendogram, we can investigate different numbers of clusters using cutree function that cuts the hierarchical clustering tree into given number of clusters.

Q12: How about we print different clustering outcomes with \(k=4, 8, 16\) using t-SNE?

3.3 Graph-based clustering

Seuratv3 adopts a graph-based clustering methodology much similar to PhenoGraph approach we discussed earlier.

FindNeighbors function performs the following steps: * (1) build a kNN graph using Euclidean distance in PCA space * (2) update the edge weights based on the shared neighbors (Jaccard index) to construct a Shared Nearest Neighbor (SNN) graph.

Once the graph is constucted, we can use FindClusters function to apply a community detection algorithm to identify subgroups. Keep in mind that Seurat uses the Louvain algorithm as a default for this step.

mgsc <- FindNeighbors(mgsc,  k.param = 20, dims = 1:30, reduction = "pca")
mgsc <- FindClusters(mgsc, resolution = 0.5)
## Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
## 
## Number of nodes: 2075
## Number of edges: 99889
## 
## Running Louvain algorithm...
## Maximum modularity in 10 random starts: 0.8377
## Number of communities: 4
## Elapsed time: 0 seconds
# Look at cluster IDs of the first 20 cells
head(Idents(mgsc), 20)
## cell_5456 cell_5457 cell_5458 cell_5459 cell_5460 cell_5461 cell_5462 cell_5463 
##         0         0         0         0         0         0         1         0 
## cell_5465 cell_5466 cell_5467 cell_5468 cell_5469 cell_5470 cell_5471 cell_5472 
##         0         1         0         3         0         0         3         3 
## cell_5473 cell_5474 cell_5475 cell_5476 
##         2         3         3         0 
## Levels: 0 1 2 3

FindClusters has a resolution parameter that sets the granularity of the downstream clustering, with increased values leading to a greater number of clusters.

The Seurat authors suggest that granularity values between 0.4-1.2 usually return good results for sc datasets of around 3K cells. For larger datasets, optimal resolution often increases for larger datasets.

Homework: Explore how different values of \(granularity\) affect the clustering results and reproduce the following plot. (Hint: Use Idents function of Seurat to save cluster ids. )

## Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
## 
## Number of nodes: 2075
## Number of edges: 99889
## 
## Running Louvain algorithm...
## Maximum modularity in 10 random starts: 0.8658
## Number of communities: 4
## Elapsed time: 0 seconds
## Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
## 
## Number of nodes: 2075
## Number of edges: 99889
## 
## Running Louvain algorithm...
## Maximum modularity in 10 random starts: 0.7710
## Number of communities: 6
## Elapsed time: 0 seconds
## Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
## 
## Number of nodes: 2075
## Number of edges: 99889
## 
## Running Louvain algorithm...
## Maximum modularity in 10 random starts: 0.7075
## Number of communities: 9
## Elapsed time: 0 seconds

LS0tCnRpdGxlOiAiQ29tcHV0YXRpb25hbCBzaW5nbGUtY2VsbCBiaW9sb2d5IGNvdXJzZSIKYXV0aG9yOiAiSGFraW1lIMOWenTDvHJrIChoLm9lenR1ZXJrQGRrZnotaGVpZGVsYmVyZy5kZSkiCmRhdGU6ICJgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVkICVCLCAlWScpYCIKb3V0cHV0OgogICAgQmlvY1N0eWxlOjpodG1sX2RvY3VtZW50OiAgICAKICAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICAgdG9jOiB5ZXMKCi0tLQoKCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQoKY29sb3JpemUgPC0gZnVuY3Rpb24oeCwgY29sb3IpIHsKICBpZiAoa25pdHI6OmlzX2xhdGV4X291dHB1dCgpKSB7CiAgICBzcHJpbnRmKCJcXHRleHRjb2xvcnslc317JXN9IiwgY29sb3IsIHgpCiAgfSBlbHNlIGlmIChrbml0cjo6aXNfaHRtbF9vdXRwdXQoKSkgewogICAgc3ByaW50ZigiPHNwYW4gc3R5bGU9J2NvbG9yOiAlczsnPiVzPC9zcGFuPiIsIGNvbG9yLCAKICAgICAgeCkKICB9IGVsc2UgeAp9CmBgYAojIERpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbiBhbmQgY2x1c3RlcmluZyBvbiBzY1JOQS1zZXEgZGF0YQoKV2Ugd2lsbCB3b3JrIG9uIHNjUk5BLXNlcSBkYXRhIG9mIG1vdXNlIGdhc3RydWxhdGlvbiBhbmQgZWFybHkgb3JnYW5vZ2VuZXNpcyBmcm9tIFtQaWp1YW4tU2FsYSBldCBhbC4sIDIwMTldKGh0dHBzOi8vd3d3Lm5hdHVyZS5jb20vYXJ0aWNsZXMvczQxNTg2LTAxOS0wOTMzLTkpLiBbVGhpcyBTaGlueSBhcHBsaWNhdGlvbl0oaHR0cHM6Ly9tYXJpb25pbGFiLmNydWsuY2FtLmFjLnVrL01vdXNlR2FzdHJ1bGF0aW9uMjAxOC8pICBwcm92aWRlcyBhbiBpbnRlcmFjdGl2ZSBpbnRlcmZhY2UgdGhhdCBhbGxvd3MgdXNlcnMgdG8gdmFsaWRhdGUgdGhlaXIgb3duIGFuYWx5c2lzIG9uIHRoaXMgZGF0YS4gWW91IGNhbiByZWFjaCB0aGUgb3JpZ2luYWwgZGF0YSBhbmQgcmVsYXRlZCBzY3JpcHRzIGZyb20gdGhlIFtHaXRodWIgcGFnZV0oaHR0cHM6Ly9naXRodWIuY29tL01hcmlvbmlMYWIvRW1icnlvVGltZWNvdXJzZTIwMTgpLgoKPiBHYXN0cnVsYXRpb24gaXMgYSBwaGFzZSBlYXJseSBpbiB0aGUgZW1icnlvbmljIGRldmVsb3BtZW50IG9mIG1vc3QgYW5pbWFscywgZHVyaW5nIHdoaWNoIHRoZSBzaW5nbGUtbGF5ZXJlZCBibGFzdHVsYSBpcyByZW9yZ2FuaXplZCBpbnRvIGEgbXVsdGlsYXllcmVkIHN0cnVjdHVyZSBrbm93biBhcyB0aGUgZ2FzdHJ1bGEgWyhXaWtpcGVkaWEsIDIwMjAtMDYtMDIpXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9HYXN0cnVsYXRpb24pLiAKCgojIyBHZXR0aW5nIGZhbWlsaWFyIHdpdGggdGhlIHByZS1wcm9jZXNzZWQgZGF0YQoKTGV0J3Mgc3RhcnQgd2l0aCBpbmNsdWRpbmcgcmVxdWlyZWQgbGlicmFyaWVzLiAKYGBge3IgbGlicmFyaWVzfQogIHN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyh7CiAgbGlicmFyeShTZXVyYXQpCiAgbGlicmFyeShnZ3Bsb3QyKQogIGxpYnJhcnkoZHBseXIpCiAgbGlicmFyeShkYXRhLnRhYmxlKQogIGxpYnJhcnkoY293cGxvdCkKICBzZXQuc2VlZCgxKQp9KQpgYGAKCmBgYHtyIGNvbnZlcnQsIGluY2x1ZGU9RkFMU0V9CiAjIC9BcHBsaWNhdGlvbnMvUlN0dWRpby5hcHAvQ29udGVudHMvTWFjT1MvUlN0dWRpbwogICMgaGVyZSBJIGFtIHJlYWRpbmcgYSBTQ0Ugb2JqZWN0IGFuZCBjb252ZXJ0IGl0IHRvIFNldXJhdCBvYmplY3QKICAjbWdzY3NjZSA8LSByZWFkUkRTKCdkYXRhL2dhc3RydWxhdGlvbi9TaW5nbGVDZWxsRXhwZXJpbWVudC5yZHMnKQogICNtZ3NjIDwtIGFzLlNldXJhdChtZ3Njc2NlLCBjb3VudHMgPSAiY291bnRzIiwgZGF0YSA9ICJsb2djb3VudHMiLCBwcm9qZWN0ID0gIm1vdXNlIGdhc3RydWxhdGlvbiIpIAogIAogICMgSSBhbSBzYXZpbmcgdGhpcyBhcyBTZXVyYXQgb2JqZWN0CiAgI3NhdmVSRFMobWdzYywgZmlsZSA9ICJkYXRhL2dhc3RydWxhdGlvbi9tZ3NjLnJkcyIpCgpgYGAKCkluIHRoZSBwcmV2aW91cyBwcmFjdGljYWwsIHdlIGhhdmUgbGVhcm5lZCB0aGUgZXNzZW50aWFsIHByZXByb2Nlc3Npbmcgc3RlcHMgZm9yIHdvcmtpbmcgd2l0aCBzY1JOQS1zZXEuIEhlcmUgd2Ugd2lsbCBzdGFydCB3b3JraW5nIHdpdGggdGhlIHByZXByb2Nlc3NlZCB2ZXJzaW9uIG9mIHRoZSBtb3VzZSBnYXN0cnVsYXRpb24gc2NSTkEtc2VxIGRhdGEuIFdlIHdpbGwgbG9hZCB0aGUgcHJlLXNhdmVkIGBTZXVyYXRgIG9iamVjdCBvZiB0aGlzIGRhdGEuIAoKKipXYXJuaW5nOioqIFRoZSBwcmUtcHJvY2Vzc2VkIG1vdXNlIGdhc3RydWxhdGlvbiBzY1JOQS1zZXEgU2V1cmF0IG9iamVjdCBpcyBsYXJnZSAoXHRpbGRlIDJHQnMpLiBJbiB0b2RheSdzIHByYWN0aWNhbCwgd2Ugd2lsbCBzdWJzZXQgdGhlIGRhdGEgZm9yIGZhc3QgY29tcHV0YXRpb24uIFlvdSBjYW4sIGhvd2V2ZXIsIHdvcmsgb24gdGhlIFtvcmlnaW5hbCBkYXRhXShmdHA6Ly9mdHAuZGtmei1oZWlkZWxiZXJnLmRlL291dGdvaW5nL2NvdXJzZV9tYXRlcmlhbF9kYXlfMS9tZ3NjLnJkcykgaWYgeW91IHdhbnQgdG8gcHJhY3Rpc2Ugd2hhdCB3ZSBsZWFybmVkIHRvZGF5LiBUaGUgW21ldGEtZGF0YV0oZnRwOi8vZnRwLmRrZnotaGVpZGVsYmVyZy5kZS9vdXRnb2luZy9jb3Vyc2VfbWF0ZXJpYWxfZGF5XzEvc2FtcGxlX21ldGFkYXRhLnR4dC5neikgaXMgcHJvdmlkZWQgc2VwZXJhdGVseS4gCgpgYGB7ciByZWFkfQogICAgbWdzYyA8LSByZWFkUkRTKCdkYXRhL2dhc3RydWxhdGlvbi9tZ3NjLnJkcycpCiAgICBtZ3NjCmBgYCAgIAogICoqUmVtaW5kZXI6KiogbnVtYmVyIG9mIHJvd3MgPSBnZW5lcyA9IGZlYXR1cmVzLCBhbmQgbnVtYmVyIG9mIGNvbHVtbnMgPSBjZWxscyA9IHNhbXBsZXMKCmBgYHtyIHNsb3R9ICAgIAogICAgIyBsZXQncyBzZWUgdGhlIGF2YWlsYWJsZSBzbG90cwogICAgc2xvdE5hbWVzKG1nc2MpCmBgYAoKTm93IGxldCdzIGxvb2sgYXQgdG8gdGhlIG1ldGFkYXRhIHRhYmxlIG9mIHRoaXMgZGF0YXNldCB0aGF0IGNvbnRhaW5zIGFuIG92ZXJ2aWV3IG9mIHRoZSBzYW1wbGVzLiAKCmBgYHtyIG1ldGFkYXRhYWRkfQogICAgIyBzZWUgaXQgaXMgZW1wdHkgZm9yIG5vdwogICAgaGVhZChtZ3NjQG1ldGEuZGF0YSwgNSkKCiAgICAjIG5vdyBsZXQncyBsb2FkIHRoZSBtZXRhZGF0YSAKICAgIG1ldGFkYXRhIDwtIGZyZWFkKCdkYXRhL2dhc3RydWxhdGlvbi9zYW1wbGVfbWV0YWRhdGEudHh0Lmd6JykgJT4lIC5bc3RyaXBwZWQ9PUZBTFNFICYgZG91YmxldD09RkFMU0VdCiAgICAKICAgICMgYW5kIGFkZCAgdGhlIG1ldGFkYXRhICB0byBvdXIgc2V1cmF0IG9iamVjdAogICAgbWdzYyA8LSBBZGRNZXRhRGF0YShtZ3NjLCBtZXRhZGF0YSA9IGRhdGEuZnJhbWUobWV0YWRhdGEsIHJvdy5uYW1lcyA9IG1ldGFkYXRhJGNlbGwpKQogICAgCiAgICAjIG5vdyBsZXQncyBzZWUgb25jZSBtb3JlCiAgICBoZWFkKG1nc2NAbWV0YS5kYXRhLCA1KQogICAgCmBgYAoKTm93IHdlIGFyZSBhYmxlIHRvIHNlZSB0aGUgYW5ub3RhdGlvbnMgKGUuZy4gY2VsbCB0eXBlKSBmb3IgZWFjaCBjZWxsLiBUaGVyZSBpcyBhbHNvIGEgY29sdW1uIG5hbWVkIGBzdGFnZWAgd2hpY2ggc2hvd3MgdGhlIGVtYnJ5b25pYyBkYXkgdGhlIGNlbGxzIHdlcmUgc2VxdWVuY2VkLiBUbyBzcGVlZCB1cCBvdXIgZXhwZXJpbWVudHMsIHdlIHdpbGwgd29yayBvbiBhIHN1YnNldCBvZiBjZWxscyB0aGF0IGJlbG9uZyB0byBzdGFnZSBgRTYuNzVgLiAKCmBgYHtyIHN1YnNldH0KICAgIG1nc2Nfc3Vic2V0IDwtIG1nc2NbICwgbWdzY0BtZXRhLmRhdGEkc3RhZ2U9PSdFNi43NSddCiAgICBtZ3NjX3N1YnNldAogICAgIyBub3cgbGV0cyBzYXZlIHRoaXMgc3Vic2V0CiAgICBzYXZlUkRTKG1nc2Nfc3Vic2V0LCBmaWxlID0gImRhdGEvZ2FzdHJ1bGF0aW9uL21nc2NfZTY3NS5yZHMiKQogICAgCiAgICBtZ3NjIDwtIG1nc2Nfc3Vic2V0CiAgCmBgYAoKVGhpcyBpcyB3aGVyZSB5b3UgYXJlIHN0YXJ0aW5nIGZyb20hIDopIE5vdyB5b3UgY2FuIGxvYWQgW2BtZ3NjX2U2NzUucmRzYF0oZnRwOi8vZnRwLmRrZnotaGVpZGVsYmVyZy5kZS9vdXRnb2luZy9jb3Vyc2VfbWF0ZXJpYWxfZGF5XzEvbWdzY19lNjc1LnJkcykgIG9iamVjdCB0byBzdGFydCB3b3JraW5nISBNYWtlIHN1cmUgdGhhdCB0aGUgb2JqZWN0IHlvdSBsb2FkZWQgaGFzIHNhbWUgbnVtYmVycyBvZiBjb2x1bW5zIGFuZCByb3dzIHdpdGggYEU2Ljc1YCBTZXVyYXQgb2JqZWN0LgoKKipRMTogTGV0J3Mgd2FybSB1cCEgQ2FuIHlvdSB0cnkgdG8gc3Vic2V0IGNlbGxzIGluIGxvY2F0aW9ucyAgJDEsIDIzLCA1MTUkIGFuZCBnZW5lcyBpbiBsb2NhdGlvbnMgJDIxLCA0NCwgMTE2JCwgcmVzcGVjdGl2ZWx5PyoqIAoKPGRldGFpbHM+CjxzdW1tYXJ5PioqQW5zd2VyKio8L3N1bW1hcnk+CmBgYHtyIHAxfQogICAgczEgPC0gbWdzY1ssIGMoMSwyMyw1MTUpXSAjIFN1YnNldCB0byBjZWxscyAxLCAyMywgNTE1CiAgICBzMiA8LSBtZ3NjW2MoMjEsNDQsMTE2KSwgXSAjIFN1YnNldCB0byBnZW5lcyAyMSwgNDQsIDExNiAKICAgIAogICAgczEKICAgIHMyCmBgYAo8L2RldGFpbHM+CgoKV2UgY2FuIHVzZSBgcm93bmFtZXNgIGFuZCBgY29sbmFtZXNgIHRvIHNlZSB0aGUgbmFtZXMgb2YgZ2VuZXMgYW5kIGNlbGxzLiAKCmBgYHtyIHAyfQojIHJvd25hbWVzID0gZ2VuZSBuYW1lcwpoZWFkKHJvd25hbWVzKG1nc2MpKQoKIyBjb2xuYW1lcyA9IHNhbXBsZS9jZWxsIG5hbWVzCmhlYWQoY29sbmFtZXMobWdzYykpCmBgYAoKKipRMjogT2JzZXJ2ZSB0aGUgY291bnQgZGF0YSBvZiB0aGUgZmlyc3QgMTUgY2VsbHMgZm9yIGdlbmVzICJFTlNNVVNHMDAwMDAwNTE5NTEiIGFuZCAiRU5TTVVTRzAwMDAwMDMzODQ1Ii4gKiogKGAgSGludDpgIHVzZSBgR2V0QXNzYXlEYXRhYCBmdW5jdGlvbi4gKQoKPGRldGFpbHM+CjxzdW1tYXJ5PioqQW5zd2VyKio8L3N1bW1hcnk+CmBgYHtyIHAzfQpHZXRBc3NheURhdGEob2JqZWN0ID0gbWdzYylbYygiRU5TTVVTRzAwMDAwMDUxOTUxIiwgIkVOU01VU0cwMDAwMDAzMzg0NSIpLCAxOjE1XQojIG1nc2NbYygiRU5TTVVTRzAwMDAwMDUxOTUxIiwgIkVOU01VU0cwMDAwMDAzMzg0NSIpLCAxOjE1XUBhc3NheXMkUk5BQGNvdW50cwpgYGAKPC9kZXRhaWxzPgoKVGhlIC4gdmFsdWVzIGluIHRoZSBtYXRyaXggcmVwcmVzZW50ICQwJHMuIFNpbmNlIG1vc3QgdmFsdWVzIGluIGFuIHNjUk5BLXNlcSBtYXRyaXggYXJlICQwJCwgU2V1cmF0IHVzZXMgYSBzcGFyc2UtbWF0cml4IHJlcHJlc2VudGF0aW9uIHRvIHNwZWVkIHVwIGNhbGN1bGF0aW9ucyBhbmQgcmVkdWNlIG1lbW9yeSB1c2FnZS4KCgojICBEaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24KCiMjIEZlYXR1cmUgc2VsZWN0aW9uOiBpZGVudGlmaWNhdGlvbiBvZiBoaWdobHkgdmFyaWFibGUgZmVhdHVyZXMKCioqUTM6T3VyIGZlYXR1cmUgc2V0IChpLmUuIG51bWJlciBvZiBnZW5lcykgYXJlIHF1aXRlIGxhcmdlLiBXaGF0IGtpbmQgb2YgZmVhdHVyZXMgZG8geW91IHRoaW5rIHNob3VsZCBiZSBpbiB0aGUgZGF0YXNldD8qKiAKCjxkZXRhaWxzPgo8c3VtbWFyeT4qKkFuc3dlcioqPC9zdW1tYXJ5PgpgciBjb2xvcml6ZSgiV2Ugd2lsbCBjaG9vc2UgYSBzdWJzZXQgb2YgZmVhdHVyZXMgdGhhdCBoYXZlIGhpZ2ggY2VsbC10by1jZWxsIHZhcmlhdGlvbiBpbiB0aGUgZGF0YXNldCAoaS5lLCBnZW5lcyB0aGF0IGFyZSBoaWdobHkgZXhwcmVzc2VkIGluIHNvbWUgY2VsbHMsIGFuZCBsb3dseSBleHByZXNzZWQgaW4gb3RoZXJzKSB3aGljaCBjYW4gaGVscCBoaWdobGlnaHRpbmcgYmlvbG9naWNhbCBzaWduYWwuIiwgInJlZCIpYCAKPC9kZXRhaWxzPgoKU2V1cmF0IGltcGxlbWVudHMgYEZpbmRWYXJpYWJsZUZlYXR1cmVzYCB0byBtb2RlbCB0aGUgbWVhbi12YXJpYW5jZSByZWxhdGlvbnNoaXAgaW4gc2luZ2xlLWNlbGwgZGF0YSB3aGljaCByZWR1Y2VzIHRvIGRpbWVuc2lvbnMgdG8gMjAwMCBieSBkZWZhdWx0LiBXZSB3aWxsIHVwZGF0ZSBgbmZlYXR1cmVzYCBhcmd1bWVudCB0byByZWR1Y2UgZGltZW5zaW9uYWxpdHkgdG8gMTAwMC4gCgpgYGB7ciBmaW5kdmFyaWFibGV9Cm1nc2MgPC0gRmluZFZhcmlhYmxlRmVhdHVyZXMobWdzYywgc2VsZWN0aW9uLm1ldGhvZCA9ICJ2c3QiLCBuZmVhdHVyZXMgPSAxMDAwKQpvcHRpb25zKHJlcHIucGxvdC53aWR0aD0xMiwgcmVwci5wbG90LmhlaWdodD02KQoKIyBJZGVudGlmeSB0aGUgMTAgbW9zdCBoaWdobHkgdmFyaWFibGUgZ2VuZXMKdG9wMTAgPC0gaGVhZChWYXJpYWJsZUZlYXR1cmVzKG1nc2MpLCAxMCkKCiMgcGxvdCB2YXJpYWJsZSBmZWF0dXJlcyAKcGxvdDEgPC0gVmFyaWFibGVGZWF0dXJlUGxvdChtZ3NjKQpMYWJlbFBvaW50cyhwbG90ID0gcGxvdDEsIHBvaW50cyA9IHRvcDEwLCByZXBlbCA9IFRSVUUsICB4bnVkZ2UgPSAwLCB5bnVkZ2UgPSAwKQoKaGVhZChIVkZJbmZvKG1nc2MpW1ZhcmlhYmxlRmVhdHVyZXMobWdzYyksIF0sIDUpCmBgYAoKIyMjIFNjYWxpbmcgdGhlIGRhdGEgCgoqKlE0OiBTZXVyYXQgaW1wbGVtZW50cyB0aGUgYFNjYWxlRGF0YWAgZnVuY3Rpb24gdG8gc2NhbGUgdGhlIGRhdGEuICBEbyB5b3UgcmVtZW1iZXIgd2h5IHdlIG5lZWQgdGhpcyBzdGVwPyoqIAoKYGBge3Igc2NhbGV9Cm1nc2MgPC0gU2NhbGVEYXRhKG1nc2MpICNWZWN0b3Igb2YgZmVhdHVyZXMgbmFtZXMgdG8gc2NhbGUvY2VudGVyLiBEZWZhdWx0IGlzIHZhcmlhYmxlIGZlYXR1cmVzLgpgYGAKClRoZSByZXN1bHRzIGFyZSBzdG9yZWQgaW5gIG1nc2NbWyJSTkEiXV1Ac2NhbGUuZGF0YWAuCgpgYGB7ciBzY2FsZWhlYWR9IAptZ3NjW1siUk5BIl1dQHNjYWxlLmRhdGFbMTo1LDE6NV0KIyBPUgojIG1nc2NAYXNzYXlzJFJOQUBzY2FsZS5kYXRhWzE6NSwxOjVdCgpgYGAKCgojIyBQcmluY2lwYWwgQ29tcG9uZW50IEFuYWx5c2lzCgpMZXQncyB1c2UgYFJ1blBDQWAgZnVuY3Rpb24gb2YgdGhlIFNldXJhdC4gUGF5IGF0dGVudGlvbiB0byB0aGUgYGZlYXR1cmVzYCBhcmd1bWVudC4gSG93IG1hbnkgZmVhdHVyZXMgd2lsbCBQQ0Egd29yayBvbj8KCmBgYHtyIHBjYSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbWdzYyA8LSBSdW5QQ0EobWdzYywgbnBjcyA9IDEwMCwgZmVhdHVyZXMgPSBWYXJpYWJsZUZlYXR1cmVzKG9iamVjdCA9IG1nc2MpKSAKYGBgCgpXZSBjYW4gZXhwbG9yZSB0aGUgY29uc3RydWN0ZWQgZW1iZWRkaW5ncyB2aWEgIGBtZ3NjQHJlZHVjdGlvbnNgLgoKYGBge3IgcmVkdWN0aW9uc30KICAgIHNsb3ROYW1lcyhtZ3NjQHJlZHVjdGlvbnNbWyJwY2EiXV0pCiAgICBkaW0obWdzY0ByZWR1Y3Rpb25zW1sicGNhIl1dQGNlbGwuZW1iZWRkaW5ncykKICAgIG1nc2NAcmVkdWN0aW9uc1tbInBjYSJdXUBjZWxsLmVtYmVkZGluZ3NbMTo1LDE6NV0KICAgICMgT1IKICAgICMgRW1iZWRkaW5ncyhtZ3NjLCByZWR1Y3Rpb24gPSAicGNhIilbMTo1LCAxOjVdCiAgICAKYGBgCgoqKlE1OiBEbyB5b3UgcmVtZW1iZXIgd2hhdCBraW5kIG9mIGluZm9ybWF0aW9uIGZlYXR1cmUubG9hZGluZ3Mgc3RvcmU/ICoqIAoKPGRldGFpbHM+CjxzdW1tYXJ5PioqQW5zd2VyKio8L3N1bW1hcnk+CmByIGNvbG9yaXplKCJmZWF0dXJlLmxvYWRpbmdzOiBzdG9yZXMgdGhlIHdlaWdodCBmb3IgZWFjaCBmZWF0dXJlIGFsb25nIGVhY2ggZGltZW5zaW9uIG9mIHRoZSBlbWJlZGRpbmcuIiwgInJlZCIpYCAKPC9kZXRhaWxzPgoKYGBge3IgcGNhdml6LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKICAjICgxKSB3ZSBjYW4gdmlzdWFsaXplIHRvcCBnZW5lcyBhc3NvY2lhdGVkIHdpdGggcGNhIGVtYmVkZGluZ3MKICBWaXpEaW1Mb2FkaW5ncyhtZ3NjLCBkaW1zID0gMToyLCByZWR1Y3Rpb24gPSAicGNhIikKICBWaXpEaW1Mb2FkaW5ncyhtZ3NjLCBkaW1zID0gMzo0LCByZWR1Y3Rpb24gPSAicGNhIikKYGBgCgoKIyMjIERldGVybWluZSB0aGUg4oCYZGltZW5zaW9uYWxpdHnigJkgb2YgdGhlIGRhdGFzZXQKCioqUTY6IE5hbWUgdGhhdCBvbmUgbWV0aG9kIHRoYXQgd2UgdXNlZCB0byBkZXRlcm1pbmUgdGhlIGRpbWVuc2lvbmFsaXR5LiBMZXQncyB0cnkgdG8gZmluZCBvdXQgaG93IG11Y2ggb2YgdGhlIHZhcmlhbmNlIGlzIGV4cGxhaW5lZCBieSB0aGUgUENzLiAqKiAoYCBIaW50OmAgY2hlY2sgYHNsb3ROYW1lcyhtZ3NjQHJlZHVjdGlvbnNbWyJwY2EiXV0pYCB3aGljaCBmZWF0dXJlIG1pZ2h0IGhlbHAgeW91IHdpdGggdGhpcy4gKQoKPGRldGFpbHM+CjxzdW1tYXJ5PioqQW5zd2VyKio8L3N1bW1hcnk+CmBgYHtyIGVsYm93IH0KcGVyY2VudCA8LSAxMDAqbWdzY0ByZWR1Y3Rpb25zW1sicGNhIl1dQHN0ZGV2XjIvc3VtKG1nc2NAcmVkdWN0aW9uc1tbInBjYSJdXUBzdGRldl4yKQoKcGVyY19kYXRhIDwtIGRhdGEuZnJhbWUocGVyY2VudD1wZXJjZW50LCBQQz0xOmxlbmd0aChwZXJjZW50KSkKZ2dwbG90KHBlcmNfZGF0YSwgYWVzKHg9UEMsIHk9cGVyY2VudCkpICsgCiAgZ2VvbV9saW5lKGFlcyh5PSBwZXJjZW50KSwgbGluZXR5cGU9MikgKyAKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPXNlcShmcm9tPTAsIHRvPTgwLCBieT0gMTApKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1yb3VuZChwZXJjZW50LCAyKSksIHNpemU9Miwgdmp1c3Q9LS41KSArIAogIHlsaW0oMCwgODApCgojIE9SIHlvdSBjYW4gZGlyZWN0bHkgdXNlIFNldXJhdCBmdW5jdGlvbiAKRWxib3dQbG90KG1nc2MsICBuZGltcyA9IDgwKQpgYGAKPC9kZXRhaWxzPgoKPCEtLQpBbm90aGVyIGFsdGVybmF0aXZlIGlzIHRvIHVzZSBgSmFja1N0cmF3YCBmdW5jdGlvbiB0byBkZWNpZGUgdGhlIG51bWJlciBvZiBkaW1lbnNpb25zLiAKClRoZSBgSmFja1N0cmF3UGxvdGAgZnVuY3Rpb24gcHJvdmlkZXMgYSB2aXN1YWxpemF0aW9uIHRvb2wgZm9yIGNvbXBhcmluZyB0aGUgZGlzdHJpYnV0aW9uIG9mIHAtdmFsdWVzIGZvciBlYWNoIFBDIHdpdGggYSB1bmlmb3JtIGRpc3RyaWJ1dGlvbiAoZGFzaGVkIGxpbmUpLiAqKlNpZ25pZmljYW50IFBDcyoqIHdpbGwgc2hvdyBhIHN0cm9uZyBlbnJpY2htZW50IG9mIGZlYXR1cmVzIHdpdGggbG93IHAtdmFsdWVzIChzb2xpZCBjdXJ2ZSBhYm92ZSB0aGUgZGFzaGVkIGxpbmUpLiAKCgpgYGB7ciBqYWNrc3RyYXd9CiAgICBtZ3NjIDwtIEphY2tTdHJhdyhtZ3NjLCBkaW1zPTUwLCBudW0ucmVwbGljYXRlID0gMTAwKSAKICAgIG1nc2MgPC0gU2NvcmVKYWNrU3RyYXcobWdzYywgZGltcyA9IDE6NTApCiAgICBKYWNrU3RyYXdQbG90KG1nc2MsIGRpbXMgPSAxOjIwKQpgYGAKClRoaXMgdGVjaG5pcXVlLCBob3dldmVyLCBtaWdodCBiZSB0aW1lLWNvbnN1bWluZyAtIGVzcGVjaWFsbHkgZm9yIGxhcmdlciBkYXRhc2V0cy4gRWxib3cgcGxvdCBpcyBhIGNvbW1vbiBwcmFjdGljZSBmb3IgaXRzIHNwZWVkLiAKCi0tPiAKCioqUTc6IE5vdyBpdCdzIHRpbWUgdG8gdmlzdWFsaXplIG91ciBkYXRhISAgR2VuZXJhdGUgdGhlIGZvbGxvd2luZyBvdXRwdXQ6IHRocmVlIDJEIHBsb3RzIHdpdGggdGhlIGZpcnN0IHNpeCBQQ3MgYW5kIHByaW50IHRoZW0gc2lkZSBieSBzaWRlLiBpLmUuIFBDMS1QQzIsIFBDMy1QQzQsIFBDNS1QQzYuICoqIChgSGludDpgIFV0aWxpemUgYERpbVBsb3RgIGFuZCBgcGxvdF9ncmlkYCBmdW5jdGlvbnMuKSAKCjxkZXRhaWxzPgo8c3VtbWFyeT4qKkFuc3dlcioqPC9zdW1tYXJ5PgpgYGB7ciBwY2FwcmludCB9CiAgIyAoMikgCiAgcGxvdF9ncmlkKG5jb2wgPSAzLAogICAgRGltUGxvdChtZ3NjLCByZWR1Y3Rpb24gPSAicGNhIiwgZGltcyA9IDE6MikgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwKICAgIERpbVBsb3QobWdzYywgcmVkdWN0aW9uID0gInBjYSIsIGRpbXMgPSAzOjQpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksCiAgICBEaW1QbG90KG1nc2MsIHJlZHVjdGlvbiA9ICJwY2EiLCBkaW1zID0gNTo2KSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICkKYGBgCjwvZGV0YWlscz4KCgojIyBOb24tbGluZWFyIGRpbWVuc2lvbmFsIHJlZHVjdGlvbiAodC1TTkUvVU1BUCkKCiMjIyB0LVNORQoKU2V1cmF0IGNvbXByaXNlcyBgUnVuVFNORWAgd2hpY2ggdXNlcyBgUnRzbmVgIGxpYnJhcnkgKHdoaWNoIHdlIHByZXZpb3VzbHkgd29ya2VkIHdpdGggaW4gdGhlIGxlY3R1cmUpLiAKCmBgYHtyIHJ1bnRzbmUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMgd2UgYXJlIHVzaW5nIHRoZSBQQ0EgZW1iZWRkaW5ncyBhcyBvdXIgaW5wdXQKbWdzYyA8LSBSdW5UU05FKG1nc2MsIHJlZHVjdGlvbj0icGNhIiwgZGltcyA9IDE6MzAsICBudGhyZWFkcyA9IDQsIG1heF9pdGVyID0gMjAwMCkKbWdzY0ByZWR1Y3Rpb25zCmBgYAoKYGBge3IgdHNuZXBsb3QsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CkRpbVBsb3QobWdzYywgcmVkdWN0aW9uID0gInRzbmUiKQoKYGBgCgoqKlE4OkRvIHlvdSByZW1lbWJlciBvbmUgb2YgdGhlIGltcG9ydGFudCBoeXBlci1wYXJhbWV0ZXJzIG9mIHQtU05FPyAgUHJpbnQgdC1TTkUgcGxvdHMgdXNpbmcgYERpbVBsb3RgIGFuZCBleHBsb3JlICBob3cgc3RydWN0dXJlIG9mIHRoZSBwcmludGVkIGRhdGEgY2hhbmdlcyB3aXRoIHRoaXMgcGFyYW1ldGVyLiBLZWVwIGRpbWVuc2lvbiBhcyAkMzAkLiAqKiAoYCBIaW50OmAgYHJlZHVjdGlvbi5uYW1lYCB3aWxsIGhlbHAgeW91IHNhdmUgeW91ciBlbWJlZGRpbmdzIHRoYXQgYXJlIHByb2R1Y2VkIGJ5IGRpZmZlcmVudCBjb25maWd1cmF0aW9ucy4gKQoKCmBgYHtyIHRzbmVwZXJwLCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCAgd2FybmluZz1GQUxTRX0KCm1nc2MgPC0gUnVuVFNORShtZ3NjLCBkaW1zID0gMTozMCwgcGVycGxleGl0eT01LCByZWR1Y3Rpb24ubmFtZSA9ICJ0c25lX3A1IiwgbnRocmVhZHMgPSA0LCBtYXhfaXRlciA9IDIwMDApCm1nc2MgPC0gUnVuVFNORShtZ3NjLCBkaW1zID0gMTozMCwgcGVycGxleGl0eT0xNSwgcmVkdWN0aW9uLm5hbWUgPSAidHNuZV9wMTUiLCBudGhyZWFkcyA9IDQsIG1heF9pdGVyID0gMjAwMCkKbWdzYyA8LSBSdW5UU05FKG1nc2MsIGRpbXMgPSAxOjMwLCBwZXJwbGV4aXR5PTMwLCByZWR1Y3Rpb24ubmFtZSA9ICJ0c25lX3AzMF9kZWZhdWx0IiwgbnRocmVhZHMgPSA0LCBtYXhfaXRlciA9IDIwMDApCm1nc2MgPC0gUnVuVFNORShtZ3NjLCBkaW1zID0gMTozMCwgcGVycGxleGl0eT01MCwgcmVkdWN0aW9uLm5hbWUgPSAidHNuZV9wNTAiLCBudGhyZWFkcyA9IDQsIG1heF9pdGVyID0gMjAwMCkKCnBsb3RfZ3JpZChucm93PTIsIG5jb2wgPSAyLAogIERpbVBsb3QobWdzYywgcmVkdWN0aW9uID0gInRzbmVfcDUiKSsgZ2d0aXRsZShsYWJlbCA9InA9NSIpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksCiAgRGltUGxvdChtZ3NjLCByZWR1Y3Rpb24gPSAidHNuZV9wMTUiKSsgZ2d0aXRsZShsYWJlbCA9InA9MTUiKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLAogIERpbVBsb3QobWdzYywgcmVkdWN0aW9uID0gInRzbmVfcDMwX2RlZmF1bHQiKSsgZ2d0aXRsZShsYWJlbCA9InA9MzAsIGRlZmF1bHQiKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLAogIERpbVBsb3QobWdzYywgcmVkdWN0aW9uID0gInRzbmVfcDUwIikrIGdndGl0bGUobGFiZWwgPSJwPTUwIikgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKQopCgpgYGAKCgojIyMgVU1BUAoKYGBge3IgdW1hcCwgLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQptZ3NjIDwtIFJ1blVNQVAobWdzYywgZGltcyA9IDE6MzApCkRpbVBsb3QobWdzYywgcmVkdWN0aW9uID0gInVtYXAiKQpgYGAKCioqSG9tZXdvcms6IEhvdyBhYm91dCB0aGUgaW1wb3J0YW50IHBhcmFtZXRlcnMgZm9yIFVNQVA/IFByaW50IGZvbGxvd2luZyBVTUFQIHBsb3RzIHVzaW5nIGBEaW1QbG90YCBhbmQgZXhwbG9yZSBob3cgc3RydWN0dXJlIG9mIHRoZSB2aXN1YWxpemVkIGRhdGEgY2hhbmdlcyB3aXRoIHRoZXNlIHR3byBwYXJhbWV0ZXJzLiBDaG9vc2UgZGltZW5zaW9ucyBiZXR3ZWVuICQxOjMwJC4gKiogCgpgYGB7ciB1bWFwbmVpZ2hib3IsIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CgptZ3NjIDwtIFJ1blVNQVAobWdzYywgcmVkdWN0aW9uLm5hbWUgPSAiVU1BUF9uNSIsIG4ubmVpZ2hib3JzPTUsIGRpbXMgPSAxOjMwKQptZ3NjIDwtIFJ1blVNQVAobWdzYywgcmVkdWN0aW9uLm5hbWUgPSAiVU1BUF9uMjAiLCBuLm5laWdoYm9ycz0yMCwgZGltcyA9IDE6MzApCm1nc2MgPC0gUnVuVU1BUChtZ3NjLCByZWR1Y3Rpb24ubmFtZSA9ICJVTUFQX24zMF9kZWZhdWx0IiwgIGRpbXMgPSAxOjMwKSAKbWdzYyA8LSBSdW5VTUFQKG1nc2MsIHJlZHVjdGlvbi5uYW1lID0gIlVNQVBfbjQwIiwgbi5uZWlnaGJvcnM9NDAsICBkaW1zID0gMTozMCkgCgpwbG90X2dyaWQobnJvdz0yLCBuY29sID0gMiwKICBEaW1QbG90KG1nc2MsIHJlZHVjdGlvbiA9ICJVTUFQX241IikrIGdndGl0bGUobGFiZWwgPSJuPTUiKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLAogIERpbVBsb3QobWdzYywgcmVkdWN0aW9uID0gIlVNQVBfbjIwIikrIGdndGl0bGUobGFiZWwgPSJuPTIwIikgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwKICBEaW1QbG90KG1nc2MsIHJlZHVjdGlvbiA9ICJVTUFQX24zMF9kZWZhdWx0IikrIGdndGl0bGUobGFiZWwgPSJuPTMwLCBkZWZhdWx0IikgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwKICBEaW1QbG90KG1nc2MsIHJlZHVjdGlvbiA9ICJVTUFQX240MCIpKyBnZ3RpdGxlKGxhYmVsID0ibj00MCIpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikKKQoKYGBgCgoKKipIb21ld29yazogUGxvdCB0aGUgcHJvamVjdGlvbnMgb2YgdGhlIGRhdGFzZXQgd2l0aCB0aHJlZSBvZiB0aGUgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uIHRlY2huaXF1ZXMgcHJpbnRlZCBzaWRlIGJ5IHNpZGUgYW5kIGV4cGxvcmUgYEF1Z21lbnRQbG90YC4gKioKCmBgYHtyIGNvbXBhcmUsIGVjaG89RkFMU0V9CiAgICBvcHRpb25zKHJlcHIucGxvdC53aWR0aD0xMiwgcmVwci5wbG90LmhlaWdodD00KQogICAgCiAgICBwMSA8LSBEaW1QbG90KG1nc2MsIHJlZHVjdGlvbiA9ICJ0c25lIiwgcHQuc2l6ZSA9IDAuMSkgKyBnZ3RpdGxlKGxhYmVsID0gInQtU05FIikgCiAgICBwMiA8LSBEaW1QbG90KG1nc2MsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgcHQuc2l6ZSA9IDAuMSkgKyBnZ3RpdGxlKGxhYmVsID0gIlVNQVAiKQogICAgcDMgPC0gRGltUGxvdChtZ3NjLCByZWR1Y3Rpb24gPSAicGNhIiwgcHQuc2l6ZSA9IDAuMSkgKyBnZ3RpdGxlKGxhYmVsID0gIlBDQSIpCiAgICAKICAgIHAxIDwtIEF1Z21lbnRQbG90KHBsb3QgPSBwMSApCiAgICBwMiA8LSBBdWdtZW50UGxvdChwbG90ID0gcDIgKQogICAgcDMgPC0gQXVnbWVudFBsb3QocGxvdCA9IHAzICkKICAgIChwMSArIHAyICsgcDMpICYgTm9MZWdlbmQoKQpgYGAKCgoKCiMgQ2x1c3RlciB0aGUgY2VsbHMKCkZvciBvdXIgc3Vic2V0IG9mIHRpbWUgcG9pbnQgRTYuNSwgd2UgaGF2ZSB0aGUgY2VsbCB0eXBlIGFubm90YXRpb24gaW5mb3JtYXRpb24uIEFsdGhvdWdoIHRoaXMgaXMgbm90IHVzdWFsbHkgdGhlIGNhc2UgaW4gbWFueSBjb21wdXRhdGlvbmFsIHByb2JsZW1zLCBpLmUuIHdlIHN0YXJ0IHdpdGhvdXQga25vd2luZyAob3IgaGF2aW5nIGEgdmVyeSB2YWd1ZSBpZGVhIG9mKSBob3cgbWFueSBjbHVzdGVycyB3ZSB3aWxsIGVuZCB1cC4gRm9yIG5vdyBsZXQncyB0YWtlIGFkdmFudGFnZSBvZiB0aGUga25vd24gYW5ub3RhdGlvbnMuIAoKKipROTogSG93IG1hbnkgJHJlYWwkIGNsYXNzZXMgd2UgaGF2ZT8gUmVwcm9kdWNlIHRoZSBmb2xsb3dpbmcgcGxvdHMgY29sb3JlZCBieSBjZWxsIHR5cGUuKiogKCBgSGludDpgIFlvdSBjYW4gbWFrZSB1c2UgYGdyb3VwLmJ5YCBhcmd1bWVudCBpbiBgRGltUGxvdGAgdG8gZXh0cmFjdCBzdG9yZWQgY2x1c3RlciBJRHMuKQoKYGBge3IgcmVhbGNsYXNzZXMsIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiAgICAjIGxlbmd0aCh1bmlxdWUobWdzY0BtZXRhLmRhdGEkY2VsbHR5cGUpKQogICAgCiAgICBvcHRpb25zKHJlcHIucGxvdC53aWR0aD0xNCwgcmVwci5wbG90LmhlaWdodD02KQogICAgcDEgPC1EaW1QbG90KG1nc2MsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAiY2VsbHR5cGUiKStnZ3RpdGxlKCJVTUFQIGNlbGwgdHlwZXMiKSAKICAgIHAyIDwtRGltUGxvdChtZ3NjLCByZWR1Y3Rpb24gPSAidHNuZSIsIGdyb3VwLmJ5ID0gImNlbGx0eXBlIikrZ2d0aXRsZSgidC1TTkUgY2VsbCB0eXBlIikgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKQogICAgcDEgKyBwMgogICAgCiAgICB1bmlxdWUobWdzY0BtZXRhLmRhdGEkY2VsbHR5cGUpCmBgYAoKCiMjIEstbWVhbnMgCgpTZXVyYXQgZG9lcyBub3Qgc3VwcG9ydCBLLW1lYW5zPyBXaGF0IGFyZSB3ZSBnb2luZyB0byBkbyBub3chPyA6KQoKKipRMTA6IFdoYXQgZG8gd2UgbmVlZCB0byBydW4gJEstbWVhbnMkPyBUcnkgdG8gcmVwcm9kdWNlIHRoZSBmb2xsb3dpbmcgcGxvdCB3aGVyZSB0cnVlIGNsYXNzZXMgYXJlIGNvbXBhcmVkIHRvIGstbWVhbnMgY2x1c3RlcnMgKCRrPTEzJCkuKiogKCBgSGludDpgIE1ha2UgdXNlIG9mIGBFbWJlZGRpbmdzYCBmdW5jdGlvbiBvciB1dGlsaXplIGByZWR1Y3Rpb25zYCB3ZSBwcmV2aW91c2x5IHVzZWQgaW4gdGhlIHByZXZpb3VzIGV4YW1wbGVzLiBZb3UgY2FuIHNhdmUgY2x1c3RlciBJRHMgdG8gYG1nc2NAbWV0YS5kYXRhYCBmaWVsZC4gKQoKCmBgYHtyIGttZWFucywgZWNobz1GQUxTRX0KcGMzMCA8LSBFbWJlZGRpbmdzKG9iamVjdCA9IG1nc2MsIHJlZHVjdGlvbiA9ICJwY2EiKSBbLCAxOjMwXQptZ3NjX2ttZWFucyA8LWttZWFucyhwYzMwLCAgaXRlci5tYXggPSAxMDAsIGNlbnRlcnM9MTMpCgpkaW0ocGMzMCkKIyBhZGQgY2x1c3RlcmluZyByZXN1bHRzIHRvICdvYmplY3RAbWV0YS5kYXRhJGttZWFucy5jbHVzdGVycycKbWdzY0BtZXRhLmRhdGEka21lYW5zLmNsdXN0ZXJzIDwtIGFzLmZhY3RvcihtZ3NjX2ttZWFucyRjbHVzdGVyKQoKcDEgPC0gRGltUGxvdChtZ3NjLCByZWR1Y3Rpb24gPSAidHNuZSIsIGdyb3VwLmJ5ID0gImNlbGx0eXBlIikrZ2d0aXRsZSgidFNORSBjZWxsIHR5cGVzIikgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKQpwMiA8LSBEaW1QbG90KG1nc2MsIHJlZHVjdGlvbiA9ICJ0c25lIiwgZ3JvdXAuYnkgPSAia21lYW5zLmNsdXN0ZXJzIiwgbGFiZWw9VFJVRSApK2dndGl0bGUoInRTTkUgay1tZWFucyBjbHVzdGVycyIpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikKCnAzIDwtIERpbVBsb3QobWdzYywgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICJjZWxsdHlwZSIpK2dndGl0bGUoIlVNQVAgY2VsbCB0eXBlcyIpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikKcDQgPC0gRGltUGxvdChtZ3NjLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gImttZWFucy5jbHVzdGVycyIsIGxhYmVsPVRSVUUpK2dndGl0bGUoIlVNQVAgay1tZWFucyBjbHVzdGVycyIpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikKCnBsb3RfZ3JpZChucm93PTIsIG5jb2wgPSAyLCAKICAgICAgICAgIHAxLCBwMiwgCiAgICAgICAgICBwMywgcDQpCgpgYGAKCioqSG9tZXdvcms6IEV4cGxvcmUgaG93IGNsdXN0ZXJzIHdvdWxkIGhhdmUgY2hhbmdlZCBpZiB5b3UgdXNlZCB0LVNORSBlbWJlZGRpbmdzIGluc3RlYWQgb2YgUENBIGFzIGFuIGlucHV0IHRvIGstbWVhbnMgYWxnb3JpdGhtLioqCgpgYGB7ciBrbWVhbnN0c25lLCBlY2hvPUZBTFNFfQp0c25lMiA8LSBFbWJlZGRpbmdzKG9iamVjdCA9IG1nc2MsIHJlZHVjdGlvbiA9ICJ0c25lIikgCm1nc2Nfa21lYW5zIDwta21lYW5zKHRzbmUyLCAgaXRlci5tYXggPSAxMDAsIGNlbnRlcnM9MTMpCgpkaW0odHNuZTIpCiMgYWRkIGNsdXN0ZXJpbmcgcmVzdWx0cyB0byAnb2JqZWN0QG1ldGEuZGF0YSRrbWVhbnMuY2x1c3RlcnMnCm1nc2NAbWV0YS5kYXRhJGttZWFucy5jbHVzdGVycy50c25lIDwtIGFzLmZhY3RvcihtZ3NjX2ttZWFucyRjbHVzdGVyKQoKCnAxIDwtIERpbVBsb3QobWdzYywgcmVkdWN0aW9uID0gInBjYSIsIGdyb3VwLmJ5ID0gImttZWFucy5jbHVzdGVycy50c25lIiwgbGFiZWw9VFJVRSkKcDIgPC0gRGltUGxvdChtZ3NjLCByZWR1Y3Rpb24gPSAidHNuZSIsIGdyb3VwLmJ5ID0gImttZWFucy5jbHVzdGVycy50c25lIiwgbGFiZWw9VFJVRSkKcDMgPC0gRGltUGxvdChtZ3NjLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gImttZWFucy5jbHVzdGVycy50c25lIiwgbGFiZWw9VFJVRSkKcDEgKyBwMiArIHAzCgpgYGAKCgojIyMgSG93IG1hbnkgY2x1c3RlcnMgc2hvdWxkIHdlIGNob29zZT8KCldlIGFscmVhZHkga25vdyB0aGUgbnVtYmVyIG9mIGNlbGwtdHlwZXMgaW4gb3VyIGRhdGEuIEJ1dCB0aGlzIGlzIG5vdCBhbHdheXMgdGhlIGNhc2UuIEhvdyB3b3VsZCB3ZSBkZWNpZGUgdGhlIG51bWJlciBvZiBjbHVzdGVycyBpZiB3ZSBkaWRuJ3Qga25vdyB0aGUgbnVtYmVyIG9mIGNsYXNzZXM/ICBXaGljaCAkayQtbWVhbnMgZmVhdHVyZSBkaWQgd2UgbG9vayBhdCB0aGUgdG8gZGVjaWRlIG51bWJlciBvZiBjbHVzdGVycz8gCgoqKkhvbWV3b3JrOiBQcmludCB0aGUgZm9sbG93aW5nIHBsb3QgdXNpbmcgdGhlIHNhbWUgZW1iZWRkaW5ncyBmcm9tIHRoZSBwcmV2aW91cyBleGFtcGxlLiAqKiAgKGBIaW50OmAgTGVjdHVyZSBub3RlczopKQoKYGBge3IgY2x1c3Rlcm51bSwgZWNobz1GQUxTRX0KICAgIHdzc18gPC0gbGlzdCgpIAogICAgY2x1cyA8LSBzZXEoNCwgNDAsIGJ5PTQpCiAgICAKICAgIGZvciAoayBpbiAxOmxlbmd0aChjbHVzKSkgewogICAgICAgIG1fa21lYW5zIDwtIGttZWFucyhwYzMwLCBpdGVyLm1heCA9IDEwMCwgY2VudGVycz1jbHVzW2tdKQogICAgICAgIHdzc18gPC0gYXBwZW5kKHdzc18sIG1fa21lYW5zJHRvdC53aXRoaW5zcykKICAgICAgICB9CiAgICAKICAgIHdzc19kYXRhIDwtIGRhdGEuZnJhbWUod3NzPXVubGlzdCh3c3NfKSwgaz1jbHVzKQogICAgZ2dwbG90KHdzc19kYXRhLCBhZXMoeD1rLCB5PXdzcykpICsgCiAgICAgIGdlb21fbGluZShhZXMoeT0gd3NzKSwgbGluZXR5cGU9MikgKyAKICAgICAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1zZXEoZnJvbT0wLCB0bz02NCwgYnk9IDQpKSArCiAgICAgIHlsaW0oMTAwMDAwLCAyNTAwMDApCmBgYAoKQ2FuIHdlIGRlY2lkZSB0aGUgbnVtYmVyIG9mIGNsdXN0ZXJzIGxvb2tpbmcgYXQgdGhpcyBwbG90PwoKIyMgSGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcKClNldXJhdCBkb2VzIG5vdCBzdXBwb3J0IGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIHRvbz8gQnV0IHdlIGNhbiBkbyBpdCEhIAoKKipRMTE6V2hpY2ggZWxlbWVudHMgZG8gd2UgbmVlZCB0byBwZXJmb3JtIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nPyBUcnkgdG8gcmVwcm9kdWNlIHRoZSBmb2xsb3dpbmcgZGVuZG9ncmFtLiBVc2UgYEV1Y2xpZGlhbmAgYXMgZGlzdGFuY2UgbWV0cmljIGFuZCBgd2FyZC5EMmAgYXMgbGlua2FnZSBtZXRob2QuICoqICAKCgpgYGB7ciBoY2x1c3QsIGVjaG89RkFMU0V9Cm1nc2NfZGlzdCA8LSBkaXN0KCBtZ3NjQHJlZHVjdGlvbnNbWyJwY2EiXV1AY2VsbC5lbWJlZGRpbmdzWywxOjMwXSwgbWV0aG9kPSJldWNsaWRlYW4iKQptZ3NjX2hjZSA8LSBoY2x1c3QobWdzY19kaXN0LCBtZXRob2Q9IndhcmQuRDIiKQoKIyBQbG90IHRoZSBvYnRhaW5lZCBkZW5kcm9ncmFtIC0gdGhpcyBtaWdodCB0YWtlIHRpbWUhIQpwbG90KG1nc2NfaGNlLCBjZXggPSAwLjYsIGhhbmcgPSAtMSwgbWFpbj0nbWdzYyBjbHVzdGVyIGRlbmRvZ3JhbScpCgpgYGAKCkxvb2tpbmcgYXQgdGhlIGRlbmRvZ3JhbSwgd2UgY2FuIGludmVzdGlnYXRlIGRpZmZlcmVudCBudW1iZXJzIG9mIGNsdXN0ZXJzIHVzaW5nIGBjdXRyZWVgIGZ1bmN0aW9uIHRoYXQgY3V0cyB0aGUgaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcgdHJlZSAgaW50byBnaXZlbiBudW1iZXIgb2YgY2x1c3RlcnMuIAoKKipRMTI6IEhvdyBhYm91dCB3ZSBwcmludCBkaWZmZXJlbnQgY2x1c3RlcmluZyBvdXRjb21lcyB3aXRoICRrPTQsIDgsIDE2JCB1c2luZyB0LVNORT8gKioKCgpgYGB7ciBoY2x1c3RwbG90LCBlY2hvPUZBTFNFfQojZXVjbGlkZWFuIGRpc3RhbmNlCm1nc2NAbWV0YS5kYXRhJGhjZV80IDwtIGN1dHJlZShtZ3NjX2hjZSxrID0gNCkKbWdzY0BtZXRhLmRhdGEkaGNlXzggPC0gY3V0cmVlKG1nc2NfaGNlLGsgPSA4KQptZ3NjQG1ldGEuZGF0YSRoY2VfMTYgPC0gY3V0cmVlKG1nc2NfaGNlLGsgPSAxNikKCm9wdGlvbnMocmVwci5wbG90LndpZHRoPTIxLCByZXByLnBsb3QuaGVpZ2h0PTUpCgpwbG90X2dyaWQobmNvbCA9IDMsCiAgRGltUGxvdChtZ3NjLCByZWR1Y3Rpb24gPSAidHNuZSIsIGdyb3VwLmJ5ID0gImhjZV80IiwgbGFiZWw9VFJVRSkrZ2d0aXRsZSgiaz00IiksCiAgRGltUGxvdChtZ3NjLCByZWR1Y3Rpb24gPSAidHNuZSIsIGdyb3VwLmJ5ID0gImhjZV84IiwgbGFiZWw9VFJVRSkrZ2d0aXRsZSgiaz04IiksCiAgRGltUGxvdChtZ3NjLCByZWR1Y3Rpb24gPSAidHNuZSIsIGdyb3VwLmJ5ID0gImhjZV8xNiIsIGxhYmVsPVRSVUUpK2dndGl0bGUoIms9MTYiKSkKYGBgCgoKIyMgR3JhcGgtYmFzZWQgY2x1c3RlcmluZwoKU2V1cmF0djMgYWRvcHRzIGEgZ3JhcGgtYmFzZWQgY2x1c3RlcmluZyBtZXRob2RvbG9neSBtdWNoIHNpbWlsYXIgdG8gUGhlbm9HcmFwaCBhcHByb2FjaCB3ZSBkaXNjdXNzZWQgZWFybGllci4KCmBGaW5kTmVpZ2hib3JzIGAgZnVuY3Rpb24gcGVyZm9ybXMgdGhlIGZvbGxvd2luZyBzdGVwczogCiogKDEpIGJ1aWxkIGEga05OIGdyYXBoIHVzaW5nIEV1Y2xpZGVhbiBkaXN0YW5jZSBpbiBQQ0Egc3BhY2UKKiAoMikgdXBkYXRlIHRoZSBlZGdlIHdlaWdodHMgYmFzZWQgb24gdGhlIHNoYXJlZCBuZWlnaGJvcnMgKEphY2NhcmQgaW5kZXgpIHRvIGNvbnN0cnVjdCBhIFNoYXJlZCBOZWFyZXN0IE5laWdoYm9yIChTTk4pIGdyYXBoLiAKCk9uY2UgdGhlIGdyYXBoIGlzIGNvbnN0dWN0ZWQsIHdlIGNhbiB1c2UgIGBGaW5kQ2x1c3RlcnMgYCBmdW5jdGlvbiAgdG8gYXBwbHkgYSBjb21tdW5pdHkgZGV0ZWN0aW9uIGFsZ29yaXRobSB0byBpZGVudGlmeSBzdWJncm91cHMuIEtlZXAgaW4gbWluZCB0aGF0IFNldXJhdCB1c2VzIHRoZSBMb3V2YWluIGFsZ29yaXRobSBhcyBhIGRlZmF1bHQgZm9yIHRoaXMgc3RlcC4KCgpgYGB7ciBzbm4sIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9Cm1nc2MgPC0gRmluZE5laWdoYm9ycyhtZ3NjLCAgay5wYXJhbSA9IDIwLCBkaW1zID0gMTozMCwgcmVkdWN0aW9uID0gInBjYSIpCm1nc2MgPC0gRmluZENsdXN0ZXJzKG1nc2MsIHJlc29sdXRpb24gPSAwLjUpCgoKIyBMb29rIGF0IGNsdXN0ZXIgSURzIG9mIHRoZSBmaXJzdCAyMCBjZWxscwpoZWFkKElkZW50cyhtZ3NjKSwgMjApCmBgYAoKYEZpbmRDbHVzdGVycyBgIGhhcyBhIHJlc29sdXRpb24gcGFyYW1ldGVyIHRoYXQgc2V0cyB0aGUgYGdyYW51bGFyaXR5YCBvZiB0aGUgZG93bnN0cmVhbSBjbHVzdGVyaW5nLCB3aXRoIGluY3JlYXNlZCB2YWx1ZXMgbGVhZGluZyB0byBhIGdyZWF0ZXIgbnVtYmVyIG9mIGNsdXN0ZXJzLiAgCgpUaGUgU2V1cmF0IGF1dGhvcnMgc3VnZ2VzdCB0aGF0ICBgZ3JhbnVsYXJpdHkgdmFsdWVzYCBiZXR3ZWVuIDAuNC0xLjIgdXN1YWxseSByZXR1cm4gZ29vZCByZXN1bHRzIGZvciBzYyBkYXRhc2V0cyBvZiBhcm91bmQgM0sgY2VsbHMuIEZvciBsYXJnZXIgZGF0YXNldHMsIG9wdGltYWwgcmVzb2x1dGlvbiBvZnRlbiBpbmNyZWFzZXMgZm9yIGxhcmdlciBkYXRhc2V0cy4gCgoKKipIb21ld29yazoqKiBFeHBsb3JlIGhvdyAgZGlmZmVyZW50IHZhbHVlcyBvZiAkZ3JhbnVsYXJpdHkkICBhZmZlY3QgdGhlIGNsdXN0ZXJpbmcgcmVzdWx0cyBhbmQgcmVwcm9kdWNlIHRoZSBmb2xsb3dpbmcgcGxvdC4gKEhpbnQ6IFVzZSBgSWRlbnRzYCBmdW5jdGlvbiBvZiBTZXVyYXQgdG8gc2F2ZSBjbHVzdGVyIGlkcy4gKQoKCmBgYHtyIHNubnBsb3QsIG1lc3NhZ2U9RkFMU0UsICB3YXJuaW5nPUZBTFNFLCBlY2hvPUZBTFNFfQogIAogIG1nc2NbWyJvcmcuaWRlbnQiXV0gPC0gSWRlbnRzKG9iamVjdCA9IG1nc2MpICMwLjUKICBtZ3NjIDwtIEZpbmRDbHVzdGVycyhtZ3NjLCByZXNvbHV0aW9uID0gMC40KQogIG1nc2NbWyJpZGVudC4wNCJdXSA8LSBJZGVudHMob2JqZWN0ID0gbWdzYykKICBtZ3NjIDwtIEZpbmRDbHVzdGVycyhtZ3NjLCByZXNvbHV0aW9uID0gMC44KQogIG1nc2NbWyJpZGVudC4wOCJdXSA8LSBJZGVudHMob2JqZWN0ID0gbWdzYykKICBtZ3NjIDwtIEZpbmRDbHVzdGVycyhtZ3NjLCByZXNvbHV0aW9uID0gMS4yKQogIG1nc2NbWyJpZGVudC4xMiJdXSA8LSBJZGVudHMob2JqZWN0ID0gbWdzYykKCiAgCiAgcGxvdF9ncmlkKG5yb3c9MiwgbmNvbCA9IDIsCiAgRGltUGxvdChtZ3NjLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gImlkZW50LjA0IiwgbGFiZWw9VFJVRSkrZ2d0aXRsZSgiU05OIHJlcz0wLjQiKSwKICBEaW1QbG90KG1nc2MsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAib3JnLmlkZW50IiwgbGFiZWw9VFJVRSkrZ2d0aXRsZSgiU05OIHJlcz0wLjUsIGRlZmF1bHQiKSwKICBEaW1QbG90KG1nc2MsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAiaWRlbnQuMDgiLCBsYWJlbD1UUlVFKStnZ3RpdGxlKCJTTk4gcmVzPTAuOCIpLAogIERpbVBsb3QobWdzYywgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICJpZGVudC4xMiIsIGxhYmVsPVRSVUUpK2dndGl0bGUoIlNOTiByZXM9MS4yIikKICAKICApCiAgCmBgYAoKCgoKCgoK